//
//  ViewController.m
//  NSOperDemo
//
//  Created by kingboyrang on 2021/11/19.
//  Copyright © 2021 wulanzhou. All rights reserved.
//

#import "ViewController.h"
#import "RCOperation.h"
#import "TCOperation.h"

@interface ViewController ()

@property (strong,nonatomic) NSOperationQueue *queue;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

//NSInvocationOperation使用
- (IBAction)btn1Click:(id)sender {
    
   //1.创建操作,封装任务
   /*
    
    单独使用NSInvocationOperation不会开启线程，默认在当前线程（指执行该方法的线程）中同步执行
    
    第三个参数object:前面方法需要接受的参数 可以为nil
    */
    
    NSLog(@"NSInvocationOperation start");
   NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operation1) object:nil];
   NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operation2) object:nil];
   NSInvocationOperation *op3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operation3) object:nil];
   
   //2.启动|执行操作
   [op1 start];
   [op2 start];
   [op3 start];
    
    NSLog(@"NSInvocationOperation end");
}

-(void)operation1{
    NSLog(@"1--%@",[NSThread currentThread]);
}

-(void)operation2{
    NSLog(@"2--%@",[NSThread currentThread]);
}

-(void)operation3{
    NSLog(@"3--%@",[NSThread currentThread]);
}

//NSBlockOperation使用
- (IBAction)btn2Click:(id)sender {
    /**
     和NSInvocationOperation相似，只是将方法替换成了Block，并可以使用addExecutionBlock追加任务
     可以得出单独使用NSBlockOperation和NSInvocationOperation一样，默认在当前线程中同步执行。
     
     
     但是使用addExecutionBlock追加的任务是并发执行的，如果这个操作中的任务数量大于1,那么会开子线程并发执行任务，并且追加的任务不一定就是子线程,也有可能是主线程。所以上述中任务1、2、3执行是可期的，有序的，但是任务4、5、6是并发执行的，不可控的。
     */
    
    NSLog(@"NSBlockOperation start");
    //1.创建操作
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1----%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2----%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3----%@",[NSThread currentThread]);
    }];
    
    //追加任务
    //注意:如果一个操作中的任务数量大于1,那么会开子线程并发执行任务
    //注意:不一定是子线程,有可能是主线程
    [op3 addExecutionBlock:^{
        NSLog(@"4---%@",[NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"5---%@",[NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"6---%@",[NSThread currentThread]);
    }];
    
    //2.启动
    [op1 start];
    [op2 start];
    [op3 start];
    
    NSLog(@"NSBlockOperation end");
}

//NSInvocationOperation和NSOperationQueue组合
- (IBAction)btn3Click:(id)sender {
    
    //创建的队列中的任务默认是异步执行的
    //1.创建操作,封装任务
         NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operation1) object:nil];
         NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operation2) object:nil];
         NSInvocationOperation *op3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operation3) object:nil];
         
         //2.创建队列
         /*
          GCD:
          串行类型:create & 主队列
          并发类型:create & 全局并发队列
          NSOperation:
          主队列:   [NSOperationQueue mainQueue] 和GCD中的主队列一样,串行队列
          非主队列: [[NSOperationQueue alloc]init]  非常特殊(同时具备并发和串行的功能)
          //默认情况下,非主队列是并发队列
          */
         NSOperationQueue *queue = [[NSOperationQueue alloc]init];
         
         //3.添加操作到队列中，addOperation方法内部已经调用了[op1 start]，不需要再手动启动
         [queue addOperation:op1];
         [queue addOperation:op2];
         [queue addOperation:op3];
}

//NSBlockOperation和NSOperationQueue组合
- (IBAction)btn4Click:(id)sender {
    
    //1.创建操作
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"1----%@",[NSThread currentThread]);
        }];
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"2----%@",[NSThread currentThread]);
        }];
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"3----%@",[NSThread currentThread]);
        }];
        //追加任务
        [op2 addExecutionBlock:^{
            NSLog(@"4----%@",[NSThread currentThread]);
        }];
        [op2 addExecutionBlock:^{
            NSLog(@"5----%@",[NSThread currentThread]);
        }];
        [op2 addExecutionBlock:^{
            NSLog(@"6----%@",[NSThread currentThread]);
        }];
        
        //2.创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        
        //3.添加操作到队列
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];
        
        
        //提供一个简便方法，使用Block直接添加任务
        //1)创建操作,2)添加操作到队列中
        [queue addOperationWithBlock:^{
            NSLog(@"7----%@",[NSThread currentThread]);
        }];
}

//自定义NSOperation使用
- (IBAction)btn5Cllick:(id)sender {
    
    //1.封装操作
        RCOperation *op1 = [[RCOperation alloc]init];
        RCOperation *op2 = [[RCOperation alloc]init];
        
        //2.启动
        [op1 start];
        [op2 start];
}

//自定义NSOperation和NSOperationQueue组合
- (IBAction)btn6Click:(id)sender {
    
    //1.封装操作
    RCOperation *op1 = [[RCOperation alloc]init];
    RCOperation *op2 = [[RCOperation alloc]init];
  
    //2.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];

    //3.添加操作到队列
    [queue addOperation:op1];
    [queue addOperation:op2];
}

//控制任务并发和串行
- (IBAction)btn7Click:(id)sender {
    
    //1.创建队列
    //默认是并发队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    //2.设置最大并发数量 maxConcurrentOperationCount
    /*
     同一时间最多有多少个任务可以执行
     串行执行任务!=只开一条线程 (线程同步)
     maxConcurrentOperationCount >1 那么就是并发队列
     maxConcurrentOperationCount == 1 那就是串行队列
     maxConcurrentOperationCount == 0  不会执行任务
     maxConcurrentOperationCount == -1 特殊意义 最大值 表示不受限制
     */
    queue.maxConcurrentOperationCount = 1;
    
    //3.封装操作
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1----%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2----%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3----%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"4----%@",[NSThread currentThread]);
    }];
    
    //4.添加到队列
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    
    /**
     
     可见，设置最大并发送=1，队列是按顺序串行执行任务的。
     注意，串行执行任务 ！= 只开一条线程 ，可以开启多条线程，只不过是以线程同步的方式执行的，就像加了互斥锁，区别队列里的任务是串行执行的还是并发执行的，不是看它开了多少条线程，而是看任务的执行方式，是有序的还是无序的。
     
     */
}

//启动任务
- (IBAction)btn8Click:(id)sender {
    
    //1.创建队列
    //默认是并发队列
    self.queue = [[NSOperationQueue alloc]init];
    
    //2.设置最大并发数量 maxConcurrentOperationCount
    self.queue.maxConcurrentOperationCount = 1;
    
    TCOperation *op = [[TCOperation alloc]init];
     TCOperation *op1 = [[TCOperation alloc]init];
    //4.添加到队列
    [self.queue addOperation:op];
    [self.queue addOperation:op1];
}
 // 暂停
- (IBAction)btnPauseClick:(id)sender {
    
    NSLog(@"开始暂停任务");
    
    //设置暂停和恢复
    //suspended设置为YES表示暂停，suspended设置为NO表示恢复
    //暂停表示不继续执行队列中的下一个任务，暂停操作是可以恢复的
    /*
     队列中的任务也是有状态的:已经执行完毕的 | 正在执行 | 排队等待状态
     */
    //不能暂停当前正在处于执行状态的任务
    [self.queue setSuspended:YES];
    
    /**
     注意：暂停操作不能使当前正在处于执行状态的任务暂停，而是该任务执行结束，后面的任务不会执行，处于排队等待状态 。例如执行2个任务，在执行第1个任务时，执行了暂停操作，第1个任务不会立即暂停，而是第1个任务执行结束后，所有任务暂停，即第2个任务不会再执行。
     
     */
}

//恢复
- (IBAction)btnRecoverClick:(id)sender {
     NSLog(@"开始恢复任务");
    //继续执行
    [self.queue setSuspended:NO];
}

 // 取消
- (IBAction)btnCancelClick:(id)sender {
    NSLog(@"取消所有任务");
    //取消队列里面的所有操作
    //取消之后，当前正在执行的操作的下一个操作将不再执行，而且永远不再执行，就像后面的所有任务都从队列里面移除了一样
    //取消操作是不可以恢复的
    //该方法内部调用了所有操作的cancel方法
    [self.queue cancelAllOperations];
}

//依赖操作
- (IBAction)btnRelyClick:(id)sender {
    
    /**
     依赖操作
     (1)NSOperation之间可以设置依赖来保证执行顺序，比如：操作A执行完后，才能执行操作B,就可以使用操作依赖
        [opB addDependency: opA]; // 操作B依赖于操作A
     
     (2)可以在不同queue的NSOperation之间创建依赖关系，比如：操作A在队列1中，操作B在队列2中，
        也可以使用addDependency来保证执行顺序。
     
     注意：不可以循环依赖
         // 不可以循环依赖
        [opB addDependency: opA];
        [opA addDependency: opB];
     */
    
        //1.创建队列
         NSOperationQueue *queue1 = [[NSOperationQueue alloc]init];
         NSOperationQueue *queue2 = [[NSOperationQueue alloc]init];
         
         //2.封装操作
         NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
             NSLog(@"1---%@",[NSThread currentThread]);
         }];
         
         NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
             NSLog(@"2---%@",[NSThread currentThread]);
         }];
         
         NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
             NSLog(@"3---%@",[NSThread currentThread]);
         }];
         
         //3.操作监听
         op3.completionBlock = ^{
             NSLog(@"3已经执行完了------%@",[NSThread currentThread]);
         };
         
         //4.添加操作依赖
         [op1 addDependency:op3]; //跨队列依赖,op1属于queue，op3属于queue2，执行完op3才执行op1
         [op2 addDependency:op1]; //执行完op1，才执行op2
    
         /**
          由依赖可知优先级：op3 > op1 > op2，
          注意：监听的操作不一定和被监听的操作同一个线程，都是异步的，只是op3执行结束，肯定会执行监听的操作。
          */
         
         //5.添加操作到队列
         [queue1 addOperation:op1];
         [queue1 addOperation:op2];
         [queue2 addOperation:op3];
    
}
//NSOperation实现线程间通信
- (IBAction)btnCommClick:(id)sender {
    
    /**
     
     设置操作依赖来实现线程间通信
     使用场景：在子线程下载两张图片，下载完毕后绘制在UIImageView中
     
     */
    
         __block UIImage *image1;
         __block UIImage *image2;
         
         //1.创建队列
         NSOperationQueue *queue = [[NSOperationQueue alloc] init];
         
         //2.封装操作下载图片1
         NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
             
             NSURL *url = [NSURL URLWithString:@"https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1907928680,2774802011&fm=26&gp=0.jpg"];
             NSData *data = [NSData dataWithContentsOfURL:url];
             
             //拿到图片数据
             image1 = [UIImage imageWithData:data];
             NSLog(@"图片1下载完成");
         }];
         
         
         //3.封装操作下载图片2
         NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
             NSURL *url = [NSURL URLWithString:@"https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1412439743,1735171648&fm=26&gp=0.jpg"];
             NSData *data = [NSData dataWithContentsOfURL:url];
             
             //拿到图片数据
             image2 = [UIImage imageWithData:data];
             NSLog(@"图片2下载完成");
         }];
         
    
    
         __weak typeof(self) weakSlef = self;
         //4.合成图片
         NSBlockOperation *drawOp = [NSBlockOperation blockOperationWithBlock:^{
             
             NSLog(@"开始绘制图片");
             
             //4.1 开启图形上下文
             UIGraphicsBeginImageContext(CGSizeMake(200, 200));
             
             //4.2 画image1
             [image1 drawInRect:CGRectMake(0, 0, 200, 100)];
             
             //4.3 画image2
             [image2 drawInRect:CGRectMake(0, 100, 200, 100)];
             
             //4.4 根据图形上下文拿到图片数据
             UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
             //        NSLog(@"%@",image);
             
             //4.5 关闭图形上下文
             UIGraphicsEndImageContext();
             
             //7.回到主线程刷新UI
             [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                 weakSlef.imageView.image = image;
                 NSLog(@"刷新UI---%@",[NSThread currentThread]);
             }];
             
         }];
         
         //5.设置操作依赖
         [drawOp addDependency:op1]; //图片绘制依赖op1
         [drawOp addDependency:op2]; //图片绘制依赖op2
         
         //6.添加操作到队列中执行
         [queue addOperation:op1];
         [queue addOperation:op2];
         [queue addOperation:drawOp];
    
}

@end
